package com.xyc.javaee.madp.util;

import cn.hutool.core.codec.Base64Decoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * @author ZY
 * @Description 加密工具
 * @date 21.6.410:02
 */
@Slf4j
@Component
public class EncryptUtil {


    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /**
     * 将base64编码后的公钥字符串转成PublicKey实例
     * @param PublicKey 公钥字符串
     * @return 公钥对象
     * @throws Exception exception
     */
    private static PublicKey getPublicKey(String PublicKey) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(PublicKey);
        //PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);私钥方法
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(keySpec);
    }

    /**
     * RSA加密
     * @param content 待加密文本
     * @param publicKey 秘钥
     * @return 密文
     * @throws Exception exception
     */
    public static void encrypt(String publicKey, byte[] content, String path) throws Exception {
        //j ava默认"RSA"="RSA/ECB/PKCS1Padding"
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKey));
        int inputLen = content.length;
        OutputStream out = new FileOutputStream(new File(path));
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(content, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(content, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        out.close();
    }

    /**
     * AES加密
     * @param jsonString 需要加密的字符串
     */
    public static byte[] aesEncrypt(String jsonString, String appId, String secret) {
        byte[] data = jsonString.getBytes(StandardCharsets.UTF_8);
        return aes(data,appId,secret,Cipher.ENCRYPT_MODE);
    }

	/**
	 * AES加密
	 * @param data 需要加密的数据
	 */
	public static byte[] aesEncrypt(byte[] data, String appId, String secret) {
		return aes(data,appId,secret,Cipher.ENCRYPT_MODE);
	}

    /**
     * AES解密
     * @param data 需要解密的byte[]数据
     */
    public static byte[] aesDecrypt(byte[] data, String appId, String secret){
        return aes(data,appId,secret,Cipher.DECRYPT_MODE);
    }

    /**
     * AES加解密
     * @param data 需要加密/解密的byte[]数据
     * @param type Cipher.ENCRYPT_MODE Cipher.DECRYPT_MODE
     */
    private static byte[] aes(byte[] data, String appId, String secret,int type){
        try {
            SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "AES");
            byte[] initParam = appId.getBytes(StandardCharsets.UTF_8);
            IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(type, secretKey, ivParameterSpec);
            return cipher.doFinal(data);
        } catch (Exception e) {
            log.error("安全配置文件加密异常",e);
            throw new RuntimeException("安全配置文件加密异常");
        }
    }

    /**
     * appSecret解密
     * @param appSecret appSecret
     * @param appId appId
     * @return 解密后的appSecret
     */
    public static String appSecretDecrypt(String appSecret, String appId){
        byte[] decode = Base64Decoder.decode(appSecret);
        byte[] secret = EncryptUtil.aesDecrypt(decode, appId, appId);
        return new String(secret);
    }
}
